package com.googlecode.imanager.client.form;

import java.util.List;

import com.googlecode.imanager.client.bridge.form.IInputList;
import com.googlecode.imanager.client.event.KeyEvent;
import com.googlecode.imanager.client.event.KeyEventType;
import com.googlecode.imanager.client.util.Selection;
import com.googlecode.imanager.client.util.SelectionMode;
import com.googlecode.imanager.client.util.SharedList;
import com.googlecode.imanager.client.util.Utils;
import com.googlecode.imanager.client.value.format.Formatter;

/**
 * @author wellsantos@wellsantos.com
 * @since 0.01
 */
public abstract class InputList<T> extends InputControl implements IInputList<T> {

    private static final long serialVersionUID = 1L;

    private List<T> items;
    private int selectedIndex;
    private Formatter<T> formatter;
    private transient int updatingItems;
    // TODO selection
    private Selection selection;

    {
        items = createItemList();
        selectedIndex = -1;
    }
    
    public InputList() {
        super();
    }
    
    protected InputList(int width, int height) {
        super(width, height);
    }

    @Override
    protected void handleDefault(KeyEvent e) {
        super.handleDefault(e);
        if (!e.isPreventDefault()){

            if (e.getEventType() == KeyEventType.KEY_DOWN){
                if (e.getKeyCode() == KeyEvent.VK_BACK_SPACE){
                    clearInput();
                }
            }
        }
    }

    public int onChanging(int selectedIndex){
        return selectedIndex;
    }
    
    protected ListControlItems createItemList(){
        return new ListControlItems();
    }

    public abstract SelectionMode getSelectionMode();
    
    @Override
    public boolean hasInput() {
        return selectedIndex != -1;
    }

    @Override
    public void clearInput() {
        setSelectedIndex(-1);
    }

    public int getSelectedIndex() {
        return selectedIndex;
    }

    public void setSelectedIndex(int selectedIndex) {
        if (doChanging(selectedIndex)) {
            getBridge().setSelectedIndex(selectedIndex);
            onChanged();
        }
    }
    
    private boolean doChanging(int selectedIndex){
        selectedIndex = onChanging(selectedIndex);
        if (selectedIndex < 0) {
            selectedIndex = -1;
        }
        if (selectedIndex >= items.size()) {
            selectedIndex = items.size() - 1;
        }
        int oldIndex = this.selectedIndex;
        this.selectedIndex = selectedIndex;
        return oldIndex != selectedIndex;
    }

    public T getSelectedItem() {
        if (selectedIndex == -1) {
            return null;
        } else {
            return getItems().get(selectedIndex);
        }
    }

    public void setSelectedItem(T item) {
        setSelectedIndex(getItems().indexOf(item));
    }
    
    public String getSelectedText(){
        return getItemText(getSelectedIndex());
    }
    
    public List<T> getItems() {
        return items;
    }

    public void setItems(List<T> items) {
        if (this.items != items){
            updatingItems++;
            this.items.clear();
            this.items.addAll(items);
            updatingItems--;
            checkSelection();
            getBridge().setItems(getItems());
        }
    }

    private void checkSelection() {
        if (selectedIndex >= getItems().size()){
            setSelectedIndex(getItems().size()-1);
        }
    }

    public Formatter<T> getFormatter() {
        return formatter;
    }

    public void setFormatter(Formatter<T> formatter) {
        if (!Utils.isEquals(this.formatter, formatter)){
            this.formatter = formatter;
            getBridge().setFormatter(formatter);
        }
    }
    
    public final int getItemCount(){
        return getItems().size();
    }

    public final int addItem(T item) {
        int index = getItems().size();
        addItem(index, item);
        return index;
    }

    @Override
    public final void addItem(int index, T item) {
        getItems().add(index, item);
    }

    @Override
    public final void removeItem(int index) {
        getItems().remove(index);
    }

    public final void removeItem(Object item) {
        getItems().remove(item);
    }

    @Override
    public final void setItem(int index, T item) {
        getItems().set(index, item);
    }
    
    public final T getItem(int index){
        return getItems().get(index);
    }
    
    public String getItemText(int index) {
        if (index >= 0 
                && index < getItems().size()){
            return getItemText(getItems().get(index));
        }
        return "";
    }
    
    public String getItemText(T item){
        String text = null;
        
        if (formatter != null){
            text = formatter.format(item);
        }
        else if (item != null){
            text = item.toString();
        }
        
        return text == null ? "" : text;
    }
    
    @Override
    @SuppressWarnings("unchecked")
    public InputList<T> clone() {
        InputList<T> c = (InputList<T>) super.clone();
        c.items = createItemList();
        return c;
    }

    @Override
    @SuppressWarnings("unchecked")
    protected IInputList<T> getBridge() {
        return (IInputList) lookupBridge();
    }
    
    /**
     * @author wellsantos@wellsantos.com
     * @since 0.01
     */
    protected class ListControlItems extends SharedList<T> {

        private static final long serialVersionUID = 1L;

        @Override
        protected void onAfterAdd(int index, T item) {
            if (updatingItems == 0){
                getBridge().addItem(index, item);
            }
        }

        @Override
        protected void onAfterRemove(int index, T item) {
            if (updatingItems == 0){
                checkSelection();
                getBridge().removeItem(index);
            }
        }

        @Override
        protected void onAfterReplace(int index, T oldItem, T newItem) {
            if (updatingItems == 0){
                getBridge().setItem(index, newItem);
            }
        }
        
    }

}
